﻿using ICodeShare.UI.Controls.Utilities;
using System.Collections;
using System.Collections.Specialized;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace ICodeShare.UI.Controls.Assists
{
    /// <summary>
    /// <see cref="ListBox"/> attached properties.
    /// </summary>
    public static class ListBoxAssist
    {
        #region Constructors

        static ListBoxAssist()
        {
            EventManager.RegisterClassHandler(typeof(ListBox), UIElement.PreviewMouseLeftButtonDownEvent,new MouseButtonEventHandler(ListBoxMouseButtonEvent));
        }

        #endregion Constructors

        #region Properties

        #region CanDeselection 是否可取消选项

        /// <summary>
        /// Get where show the shadow for waiting data
        /// </summary>
        [AttachedPropertyBrowsableForType(typeof(ListBox))]
        public static bool GetCanDeselection(DependencyObject element)
        {
            return (bool)element.GetValue(CanDeselectionProperty);
        }

        /// <summary>
        /// Set where show the shadow for waiting data
        /// </summary>
        public static void SetCanDeselection(DependencyObject element, bool value)
        {
            element.SetValue(CanDeselectionProperty, value);
        }

        /// <summary>
        /// DependencyProperty for <see cref="CanDeselection" /> property.
        /// </summary>
        public static readonly DependencyProperty CanDeselectionProperty = DependencyProperty.RegisterAttached(
            "CanDeselection", 
            typeof(bool),
            typeof(ListBoxAssist), 
            new UIPropertyMetadata(false));

        #endregion CanDeselection 是否可取消选项

        #region BindableSelectedItems

        public static INotifyCollectionChanged GetBindableSelectedItems(ListBox listBox)
        {
            return (INotifyCollectionChanged)listBox.GetValue(BindableSelectedItemsProperty);
        }

        public static void SetBindableSelectedItems(ListBox listBox, INotifyCollectionChanged value)
        {
            listBox.SetValue(BindableSelectedItemsProperty, value);
        }

        public static readonly DependencyProperty BindableSelectedItemsProperty = DependencyProperty.RegisterAttached(
          "BindableSelectedItems",
          typeof(INotifyCollectionChanged),
          typeof(ListBoxAssist),
          new PropertyMetadata(null, OnBindableSelectedItemsChanged));

        private static void OnBindableSelectedItemsChanged(
           DependencyObject dependencyObject,
           DependencyPropertyChangedEventArgs e)
        {
            ListBox listBox = (ListBox)dependencyObject;

            INotifyCollectionChanged notifyCollectionChanged = GetBindableSelectedItems(listBox);
            if (notifyCollectionChanged != null)
            {
                listBox.SelectionChanged +=
                    (sender, e2) =>
                    {
                        if (!GetSuppressSelectedItemsChange(listBox))
                        {
                            SetSuppressSelectedItemsChange(listBox, true);

                            IList list = notifyCollectionChanged as IList;

                            if (e2.AddedItems != null)
                            {
                                foreach (object item in e2.AddedItems)
                                {
                                    list.Add(item);
                                }
                            }

                            if (e2.RemovedItems != null)
                            {
                                foreach (object item in e2.RemovedItems)
                                {
                                    list.Remove(item);
                                }
                            }

                            SetSuppressSelectedItemsChange(listBox, false);
                        }
                    };

                notifyCollectionChanged.CollectionChanged +=
                    (sender, e2) =>
                    {
                        if (!GetSuppressSelectedItemsChange(listBox))
                        {
                            SetSuppressSelectedItemsChange(listBox, true);

                            if (e2.Action == NotifyCollectionChangedAction.Reset)
                            {
                                listBox.SelectedItems.Clear();
                            }
                            else
                            {
                                if (e2.NewItems != null)
                                {
                                    foreach (object item in e2.NewItems)
                                    {
                                        listBox.SelectedItems.Add(item);
                                    }
                                }

                                if (e2.OldItems != null)
                                {
                                    foreach (object item in e2.OldItems)
                                    {
                                        listBox.SelectedItems.Remove(item);
                                    }
                                }
                            }

                            SetSuppressSelectedItemsChange(listBox, false);
                        }
                    };
            }
        }

        #endregion

        #region SuppressSelectedItemsChange

        private static bool GetSuppressSelectedItemsChange(ListBox listBox)
        {
            return (bool)listBox.GetValue(SuppressSelectedItemsChangeProperty);
        }

        private static void SetSuppressSelectedItemsChange(ListBox listBox, bool value)
        {
            listBox.SetValue(SuppressSelectedItemsChangePropertyKey, value);
        }

        private static readonly DependencyPropertyKey SuppressSelectedItemsChangePropertyKey = DependencyProperty.RegisterAttachedReadOnly(
           "SuppressSelectedItemsChange",
           typeof(bool),
           typeof(ListBoxAssist),
           new PropertyMetadata(false));

        public static readonly DependencyProperty SuppressSelectedItemsChangeProperty = SuppressSelectedItemsChangePropertyKey.DependencyProperty;

        #endregion

        #region IsToggle 是否带选中效果
        public static void SetIsToggle(DependencyObject element, bool value)
        {
            element.SetValue(IsToggleProperty, value);
        }

        public static bool GetIsToggle(DependencyObject element)
        {
            return (bool)element.GetValue(IsToggleProperty);
        }

        public static readonly DependencyProperty IsToggleProperty = DependencyProperty.RegisterAttached(
           "IsToggle",
           typeof(bool),
           typeof(ListBoxAssist),
           new FrameworkPropertyMetadata(default(bool)));

        #endregion

        #endregion

        #region Methods

        private static void ListBoxMouseButtonEvent(object sender, MouseButtonEventArgs mouseButtonEventArgs)
        {
            var senderElement = (UIElement)sender;

            if (!GetIsToggle(senderElement)) return;

            var point = mouseButtonEventArgs.GetPosition(senderElement);
            var result = VisualTreeHelper.HitTest(senderElement, point);

            ListBoxItem listBoxItem = null;
            Ripple ripple = null;
            foreach (var dependencyObject in result.VisualHit.GetVisualAncestry().TakeWhile(_ => listBoxItem == null))
            {
                listBoxItem = dependencyObject as ListBoxItem;
                if (ripple == null)
                    ripple = dependencyObject as Ripple;
            }

            if (listBoxItem == null) return;

            listBoxItem.SetCurrentValue(ListBoxItem.IsSelectedProperty, !listBoxItem.IsSelected);
            mouseButtonEventArgs.Handled = true;

            if (ripple != null && listBoxItem.IsSelected)
            {
                ripple.RaiseEvent(new MouseButtonEventArgs(mouseButtonEventArgs.MouseDevice, mouseButtonEventArgs.Timestamp, mouseButtonEventArgs.ChangedButton) { RoutedEvent = Control.PreviewMouseLeftButtonDownEvent, Source = ripple }
                );
            }
        }


        #endregion
    }
}